package com.loop.netty.simple;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

import java.util.concurrent.TimeUnit;

/**
 * 自定义一个Handler，需要继承ChannelInboundHandlerAdapter（netty规定）
 *
 * @author zhangxiaolu
 * @since 2023-09-10 22:11
 */
public class SimpleNettyServerHandler extends ChannelInboundHandlerAdapter {

    // 读取数据事件（这里我们可以读取客户端发送的消息）

    /**
     * @param ctx 上下文对象，含有管道pipeline，通道channel，地址
     * @param msg 就是客户端发送的数据 默认是Object
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 比如这里有一个非常耗时的业务 → 异步执行 → 提交该channel对应的 NIOEventLoop 的 taskQueue 中

        // 防止阻塞
        // 解决方案1 用户程序自定义的普通任务
        ctx.channel().eventLoop().execute(() -> {
            try {
                Thread.sleep(5 * 1000);
                ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端~1", CharsetUtil.UTF_8));
            } catch (InterruptedException e) {
                System.out.println("发生异常：" + e.getMessage());
                throw new RuntimeException(e);
            }
        });

        ctx.channel().eventLoop().execute(() -> {
            try {
                Thread.sleep(10 * 1000);
                ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端~3", CharsetUtil.UTF_8));
            } catch (InterruptedException e) {
                System.out.println("发生异常：" + e.getMessage());
                throw new RuntimeException(e);
            }
        });
//        System.out.println("NettyServerHandler.channelRead()");
//        System.out.println("server ctx:" + ctx);
//        System.out.println("msg:" + msg);
//        // 将msg转成ByteBuf（netty提供的类，比nio提供的性能更高）
//        ByteBuf byteBuf = (ByteBuf) msg;
//        System.out.println("客户端发送的消息是：" + byteBuf.toString(CharsetUtil.UTF_8));
//        System.out.println("客户端的地址是：" + ctx.channel().remoteAddress());

        // 解决方案2 用户自定义定时任务 → 该任务提交到scheduleTaskQueue 中
        ctx.channel().eventLoop().schedule(() -> {
            ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端~4", CharsetUtil.UTF_8));
        }, 5, TimeUnit.SECONDS);

        // 可以使用一个集合管理SocketChannel，在推送消息时，可以将业务加入到各个channel对应的NIOEventLoop的taskQueue或scheduleTaskQueue中

    }

    // 数据读取完毕事件
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        // write和flush方法的合并，将数据写入到缓存，并刷新
        // 刷新是必须的，否则数据不会被写入到Socket通道
        // 刷新之后，数据才能发送到客户端
        // 一般讲，我们对这个发送的数据进行编码
        System.out.println("NettyServerHandler.channelReadComplete()");
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端~2", CharsetUtil.UTF_8));

    }

    // 出现异常事件
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("NettyServerHandler.exceptionCaught()");
        ctx.close();
    }
}
